package ace.tool.generate.code.impl.manager;

import ace.tool.generate.code.impl.generator.DefaultGenerator;
import ace.tool.generate.code.impl.manager.common.EntityModelManager;
import ace.tool.generate.code.impl.resolver.scheme.model.bo.ApiProject;
import ace.tool.generate.code.impl.resolver.scheme.model.bo.GenerateContext;
import ace.tool.generate.code.impl.resolver.scheme.model.bo.api.ApiClassInfo;
import ace.tool.generate.code.impl.resolver.scheme.model.bo.api.AuthorityConstantClassInfo;
import ace.tool.generate.code.impl.resolver.scheme.model.bo.api.ConstantClassInfo;
import ace.tool.generate.code.impl.resolver.scheme.model.bo.api.DtoClassInfo;
import ace.tool.generate.code.impl.resolver.scheme.model.bo.api.EnumClassInfo;
import ace.tool.generate.code.impl.resolver.scheme.model.bo.core.EntityClassInfo;
import java.io.File;
import java.util.HashMap;
import java.util.Map;
import lombok.SneakyThrows;
import org.apache.commons.io.FileUtils;

/**
 * @author caspar
 * @date 2024/1/11 10:12 api project generator
 */
public class ApiProjectManager {

  private final GenerateContext context;

  public ApiProjectManager(GenerateContext context) {
    this.context = context;
  }

  @SneakyThrows
  public void generate() {

    this.generatePom();

    this.generateReadme();

    this.generateApis();

    this.generateModelDtos();

    this.generateEnums();

    this.generateConstant();

    this.generateAuthorityConstant();

    this.generateResources();
  }

  private void generateAuthorityConstant() {
    ApiProject apiProject = context.getModuleProject().getApiProject();
    AuthorityConstantClassInfo authorityConstantClassInfo =
        apiProject.getAuthorityConstantClassInfo();
    Map<String, Object> model = new HashMap<>();
    model.put("packageFullName", authorityConstantClassInfo.getInfo().getClassPackageFullName());
    model.put("className", authorityConstantClassInfo.getInfo().getClassName());
    model.put("fields", authorityConstantClassInfo.getFields());

    String templateFile = "/schema/api-project/ApiProjectAuthorityConstantClass.ftl";
    String outputFileFullName = authorityConstantClassInfo.getInfo().getClassFileFullName();
    new DefaultGenerator(templateFile, outputFileFullName).generate(model);
  }

  @SneakyThrows
  private void generateResources() {
    FileUtils.forceMkdir(
        new File(context.getModuleProject().getApiProject().getInfo().getResourcesRootDir()));
  }

  private void generateConstant() {
    ApiProject apiProject = context.getModuleProject().getApiProject();
    ConstantClassInfo constantClassInfo = apiProject.getConstantClassInfo();
    String templateFile = "/schema/api-project/ApiProjectConstantClass.ftl";
    String outputFileFullName = constantClassInfo.getInfo().getClassFileFullName();
    Map<String, Object> model = new HashMap<>();
    model.put("packageFullName", constantClassInfo.getInfo().getClassPackageFullName());
    model.put("className", constantClassInfo.getInfo().getClassName());
    model.put("projectModuleName", apiProject.getInfo().getProjectModuleName());
    model.put("projectModuleDisplayName", apiProject.getInfo().getProjectModuleDisplayName());
    new DefaultGenerator(templateFile, outputFileFullName).generate(model);
  }

  private void generateEnums() {
    context.getModuleProject().getApiProject().getEnumClassInfos().forEach(this::generateEnum);
  }

  private void generateEnum(EnumClassInfo enumClassInfo) {
    String templateFile = "/schema/api-project/ApiProjectEnumClass.ftl";
    String outputFileFullName = enumClassInfo.getInfo().getClassFileFullName();
    Map<String, Object> model = new HashMap<>();
    model.put("packageFullName", enumClassInfo.getInfo().getClassPackageFullName());
    model.put("remark", enumClassInfo.getRemark());
    model.put("className", enumClassInfo.getInfo().getClassName());
    model.put("codeClass", enumClassInfo.getCodeClass());
    model.put("fields", enumClassInfo.getFields());

    new DefaultGenerator(templateFile, outputFileFullName).generate(model);
  }

  private void generateModelDtos() {
    context.getModuleProject().getApiProject().getDtoClasses().forEach(this::generateModelDto);
  }

  private void generateModelDto(DtoClassInfo dtoClassInfo) {
    EntityModelManager entityModelManager = new EntityModelManager();
    EntityClassInfo entityClassInfo = entityModelManager.getEntityClassInfoByEntityModelId(
        context, dtoClassInfo.getEntityModelId());
    String templateFile = "/schema/api-project/ApiProjectDtoClass.ftl";
    String outputFileFullName = dtoClassInfo.getInfo().getClassFileFullName();
    Map<String, Object> model = new HashMap<>();
    model.put("packageFullName", dtoClassInfo.getInfo().getClassPackageFullName());
    model.put("remark", entityClassInfo.getModel().getRemark());
    model.put("className", dtoClassInfo.getInfo().getClassName());
    model.put("fields", dtoClassInfo.getFieldInfos());
    model.put("tableName", dtoClassInfo.getTableName());
    model.put("source", dtoClassInfo);

    new DefaultGenerator(templateFile, outputFileFullName).generate(model);
  }

  private void generateApis() {
    context.getModuleProject().getApiProject().getApiClasses().forEach(this::generateApi);
  }

  private void generateApi(ApiClassInfo apiClassInfo) {
    EntityModelManager entityModelManager = new EntityModelManager();
    EntityClassInfo entityClassInfo =
        entityModelManager.getEntityClassInfoByEntityModelId(
            context, apiClassInfo.getEntityModelId());
    DtoClassInfo dtoClassInfo =
        entityModelManager.getDtoClassInfoByEntityModelId(context, apiClassInfo.getEntityModelId());
    String templateFile = "/schema/api-project/ApiProjectApiClass.ftl";
    String outputFileFullName = apiClassInfo.getInfo().getClassFileFullName();
    Map<String, String> model = new HashMap<>();
    model.put("apiPackageFullName", apiClassInfo.getInfo().getClassPackageFullName());
    model.put("dtoClassFullName", dtoClassInfo.getInfo().getClassFullName());
    model.put("dtoClassName", dtoClassInfo.getInfo().getClassName());
    model.put("entityIdType", entityClassInfo.getModel().getEntityIdType());
    model.put("remark", entityClassInfo.getModel().getRemark());
    model.put("apiClassName", apiClassInfo.getInfo().getClassName());

    new DefaultGenerator(templateFile, outputFileFullName).generate(model);
  }

  private void generateReadme() {
    Map<String, String> model = new HashMap<>();
    String templateFile = "/schema/api-project/ApiProjectReadme.ftl";
    String outputFileFullName =
        context.getModuleProject().getApiProject().getInfo().getReadmeFileFullName();
    new DefaultGenerator(templateFile, outputFileFullName).generate(model);
  }

  private void generatePom() {
    Map<String, String> model = new HashMap<>();
    model.put("projectMavenArtifactId", context.getModuleProject().getInfo().getMavenArtifactId());
    model.put(
        "apiProjectMavenArtifactId",
        context.getModuleProject().getApiProject().getInfo().getMavenArtifactId());
    String templateFile = "/schema/api-project/ApiProjectPom.ftl";
    String outputFileFullName =
        context.getModuleProject().getApiProject().getInfo().getPomFileFullName();
    new DefaultGenerator(templateFile, outputFileFullName).generate(model);
  }
}
